#include <am.h>
#include <nemu.h>
#include <klib.h>

#define AUDIO_FREQ_ADDR      (AUDIO_ADDR + 0x00)
#define AUDIO_CHANNELS_ADDR  (AUDIO_ADDR + 0x04)
#define AUDIO_SAMPLES_ADDR   (AUDIO_ADDR + 0x08)
#define AUDIO_SBUF_SIZE_ADDR (AUDIO_ADDR + 0x0c)
#define AUDIO_INIT_ADDR      (AUDIO_ADDR + 0x10)
#define AUDIO_COUNT_ADDR     (AUDIO_ADDR + 0x14)
#define AUDIO_WOFF_ADDR      (AUDIO_ADDR + 0x18)

static void audio_write(uint8_t *buf, size_t len) {
  uint8_t *p = buf;
  uint32_t bufsize = inl(AUDIO_SBUF_SIZE_ADDR) + 1;
  uint32_t write_offset, count;
  while (len) {
    write_offset = inl(AUDIO_WOFF_ADDR);
    while((count = inl(AUDIO_COUNT_ADDR)) == bufsize - 1);
    count = bufsize - 1 - count;
    count = len < count? len : count;
    for(size_t i = 0; i < count; i++) {
      // outl(AUDIO_SBUF_ADDR + (write_offset) % bufsize, *p);
      write_offset++;
      p++;
    }
    len -= count;
    outl(AUDIO_WOFF_ADDR,  (write_offset) % bufsize);
  }
}

void __am_audio_init() {
  outl(AUDIO_INIT_ADDR, 1);
}

void __am_audio_config(AM_AUDIO_CONFIG_T *cfg) {
  cfg->present = true;
  cfg->bufsize = inl(AUDIO_SBUF_SIZE_ADDR);
}

void __am_audio_ctrl(AM_AUDIO_CTRL_T *ctrl) {
  outl(AUDIO_FREQ_ADDR, ctrl->freq);
  outl(AUDIO_CHANNELS_ADDR, ctrl->channels);
  outl(AUDIO_SAMPLES_ADDR, ctrl->samples);
}

void __am_audio_status(AM_AUDIO_STATUS_T *stat) {
  stat->count = inl(AUDIO_COUNT_ADDR);
}

void __am_audio_play(AM_AUDIO_PLAY_T *ctl) {
  size_t len = ctl->buf.end - ctl->buf.start;
  audio_write(ctl->buf.start, len);
}
